Skip to content

storage: fix fetchConsumer panic when a topic is deleted and recreated concurrently - #865

Open
ZayanKhan-12 wants to merge 1 commit into
linkedin:masterfrom
ZayanKhan-12:fix/fetch-consumer-panic-recreated-topic
Open

storage: fix fetchConsumer panic when a topic is deleted and recreated concurrently#865
ZayanKhan-12 wants to merge 1 commit into
linkedin:masterfrom
ZayanKhan-12:fix/fetch-consumer-panic-recreated-topic

Conversation

@ZayanKhan-12

Copy link
Copy Markdown

Fixes #864

Problem

fetchConsumer snapshots the consumer group's topics under consumerLock, releases it, then reads broker offsets under brokerLock. Because storage requests are spread across workers (SetDeleteTopic/SetBrokerOffset go to a random worker, SetConsumerOffset/FetchConsumer to a hashed one), a topic delete + recreate can interleave between the two phases, leaving the broker view inconsistent with the consumer snapshot:

  • A partition ring can exist with no stored offsets (a consumer commit that landed between the two halves of deleteTopic survives; addBrokerOffset grows the recreated topic's partition list with empty rings). partition.BrokerOffsets is then empty, and since getConsumerTopicList returns Offsets at full ring length (nils included), the len(partition.Offsets) > 0 guard passes and BrokerOffsets[len(BrokerOffsets)-1] panics with index out of range [-1] (inmemory.go:871).
  • The recreated topic can have fewer partitions than the snapshot, so topicMap[p] (inmemory.go:863) indexes out of range.

Either way the whole process dies. Full analysis in #864.

Fix

Mirror the existing "topic may have just been deleted" handling a few lines above:

  • skip partitions the broker no longer knows about (p >= len(topicMap))
  • only compute CurrentLag when at least one broker offset is present

Consumer offsets are still returned in both cases.

Testing

  • Two regression tests construct the post-race states (state construction in the same style as TestInMemoryStorage_fetchConsumer_Expired). Both reproduce the exact panic before the fix:
    panic: runtime error: index out of range [-1]
      .../core/internal/storage/inmemory.go:871
    
    and pass after it.
  • go test -race ./...: all packages pass.
  • go vet ./... and gofmt clean.

🤖 Generated with Claude Code

…d concurrently

fetchConsumer snapshots the consumer group's topics under the consumer
lock, releases it, and then reads broker offsets under the broker lock.
Storage requests are spread across workers (SetDeleteTopic and
SetBrokerOffset go to a random worker, SetConsumerOffset and
FetchConsumer to a hashed one), so a topic can be deleted and recreated
between the two phases. The broker view of the topic then disagrees
with the consumer snapshot in two ways that panic the process:

- a partition ring can exist with no stored offsets yet (deleteTopic
  removes consumer topics and broker state under separate locks, so a
  consumer commit that lands in between survives the delete; when the
  recreated topic's partition list is grown by addBrokerOffset, rings
  for partitions without received offsets are empty), making
  partition.BrokerOffsets empty and the read of
  BrokerOffsets[len(BrokerOffsets)-1] panic with index out of range [-1]

- the recreated topic can have fewer partitions than the consumer
  snapshot, so indexing topicMap[p] panics with index out of range

Guard both: skip partitions the broker no longer knows about, and only
compute current lag when at least one broker offset is present. The
consumer offsets are still returned either way, consistent with how a
fully deleted topic is already handled.

Both states are covered by regression tests that reproduce the exact
panic prior to this fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ZayanKhan-12
ZayanKhan-12 requested a review from bai as a code owner July 22, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fetchConsumer panics (index out of range) when a topic is deleted and recreated concurrently

1 participant